/*
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *  Copyright (C) 2006-2008 XNeur Team
 *
 */

#ifdef HAVE_CONFIG_H
#  include "config.h"
#endif

#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <getopt.h>
#include <unistd.h>
#include <string.h>
#include <locale.h>

#include <sys/time.h>
#include <sys/resource.h>

#include "xnconfig.h"

#include "xprogram.h"
#include "xbtable.h"
#include "xswitchlang.h"
#include "xkeymap.h"

#include "types.h"
#include "utils.h"
#include "list_char.h"
#include "log.h"
#include "colors.h"

struct _xkeylog_config *xconfig = NULL;
static struct _xprogram *xprogram = NULL;

static int xkeylog_check_lock = TRUE;
static int xkeylog_generate_proto = FALSE;

static void xkeylog_reload(int status);

static void xkeylog_check_config_version(int final)
{
	log_message(LOG, _("Checking configuration file version..."));

	if (xconfig->version != NULL && strcmp(xconfig->version, VERSION) == 0)
	{
		log_message(LOG, _("User configuration file version is OK!"));
		return;
	}

	log_message(ERROR, _("Configuration file version is out of date!"));

	if (final)
	{
		xconfig->uninit(xconfig);
		exit(EXIT_FAILURE);
	}

	if (!xconfig->replace(xconfig))
	{
		log_message(ERROR, _("Default configuration file not founded in system! Please, reinstall XNeur!"));
		xconfig->uninit(xconfig);
		exit(EXIT_FAILURE);
	}

	log_message(LOG, _("Configuration file replaced to default one"));

	xkeylog_reload(0);
}

static void xkeylog_init(void)
{
	if (!print_keyboard_groups())
	{
		xconfig->uninit(xconfig);
		exit(EXIT_FAILURE);
	}

	bind_manual_actions();
	bind_user_actions();
}

static void xkeylog_load_config(int final)
{
	log_message(LOG, _("Loading configuration"));

	if (!xconfig->load(xconfig))
	{
		log_message(ERROR, _("Configuration file damaged! Please, remove old file before starting xkeylog!"));
		xconfig->uninit(xconfig);
		exit(EXIT_FAILURE);
	}

	xkeylog_check_config_version(final);

	log_message(LOG, _("Log level is set to %s"), xconfig->get_log_level_name(xconfig));
	log_message(LOG, _("Total detected %d languages"), xconfig->total_languages);

	log_message(DEBUG, _("Configuration load complete"));

	log_message(LOG, _("Mouse processing mode set to %s"), xconfig->get_bool_name(xconfig->grab_mouse));
	log_message(LOG, _("Save selection mode set to %s"), xconfig->get_bool_name(xconfig->save_selection));
	log_message(LOG, _("Logging keyboard mode set to %s"), xconfig->get_bool_name(xconfig->save_keyboard_log));
	log_message(LOG, _("Flush internal buffer when pressed Enter or Tab mode set to %s"), xconfig->get_bool_name(xconfig->flush_buffer_when_press_enter));
	log_message(LOG, _("Don't process word when pressed Enter or Tab mode set to %s"), xconfig->get_bool_name(xconfig->dont_process_when_press_enter));
}

static void xkeylog_set_lock(void)
{
	if (xkeylog_check_lock == TRUE)
	{
		int locked_pid = xconfig->get_pid(xconfig);
		if (locked_pid != -1)
		{
			log_message(ERROR, _(PACKAGE " already running with pid %d"), locked_pid);
			exit(EXIT_FAILURE);
		}
	}

	int process_id = getpid();
	xconfig->set_pid(xconfig, process_id);

	int priority = getpriority(PRIO_PROCESS, process_id); 
	
	setpriority(PRIO_PROCESS, process_id, -19); 
	priority = getpriority(PRIO_PROCESS, process_id);
	log_message(DEBUG, _(PACKAGE" pid is %d with nice %d"), process_id, priority); 
}

static void xkeylog_cleanup(void)
{
	if (xprogram != NULL)
		xprogram->uninit(xprogram);

	log_message(DEBUG, _("Current program info is freed"));

	if (xconfig != NULL)
	{
		xconfig->set_pid(xconfig, 0);
		xconfig->uninit(xconfig);
	}
	log_message(DEBUG, _("Current configuration data is freed"));

#ifdef WITH_DEBUG
	xndebug_uninit();
#endif
}

static void xkeylog_terminate(int status)
{
	if (status){}

	log_message(DEBUG, _("Caught SIGTERM/SIGINT, terminating"));
	sleep(1);
	
	xkeylog_cleanup();

	exit(EXIT_SUCCESS);
}

static void xkeylog_reload(int status)
{
	status = status; // To prevent warnings
	log_message(LOG, _("Caught SIGHUP, reloading configuration file"));
	
	if (xconfig != NULL)
		xconfig->uninit(xconfig);

	xconfig = xkeylog_config_init();
	if (xconfig == NULL)
	{
		log_message(ERROR, _("Can't init libxnconfig"));
		exit(EXIT_FAILURE);
	}

	xkeylog_load_config(TRUE);
	xkeylog_init();
}

static void xkeylog_usage(void)
{
	printf("\nXneur - automatic keyboard switcher (version %s) \n", VERSION);
	printf("usage: xkeylog [options]\n");
	printf("  where options are:\n");
	printf("\n");
	printf("  -v, --version           Print version and exit\n");
	printf("  -h, --help              This help!\n");
	printf("  -a, --about             About for " PACKAGE "\n");
	printf("  -f, --force             Skip check for other instances of " PACKAGE " runned\n");
	printf("  -g, --generate          Generate proto for new language. THIS OPTION FOR DEVELOPERS ONLY!\n");
}

static void xkeylog_version(void)
{
	printf("\nXneur - automatic keyboard switcher (version %s) \n", VERSION);
	printf("mailto: andrewcrew@rambler.ru\n\n");
	printf("web: http://www.xkeylog.ru/\n");
}

static void xkeylog_about(void)
{
	printf("\nXneur\n");
	printf("Automatic keyboard switcher (version %s) \n", VERSION);
	printf("It's program like Punto Switcher. \n");
	printf("This utility is made for X Window System.\n\n");
	printf("mailto: andrewcrew@rambler.ru\n");
	printf("web: http://www.xkeylog.ru/\n");
}

static void xkeylog_get_options(int argc, char *argv[])
{
	static struct option longopts[] =
	{
			{ "version",		no_argument,	NULL,	'v' },
			{ "help",		no_argument,	NULL,	'h' },
			{ "about",		no_argument,	NULL,	'a' },
			{ "force",		no_argument,	NULL,	'f' },
			{ "generate",	no_argument,	NULL,	'g' },
			{ NULL,			0,		NULL,	0 }
	};

	int opted = FALSE;
	int opt;
	while ((opt = getopt_long(argc, argv, "vhafg", longopts, NULL)) != -1)
	{
		opted = TRUE;
		switch (opt)
		{
			case 'v':
			{
				xkeylog_version();
				break;
			}
			case 'a':
			{
				xkeylog_about();
				break;
			}
			case 'f':
			{
				xkeylog_check_lock = FALSE;
				opted = FALSE;
				break;
			}
			case 'g':
			{
				xkeylog_generate_proto = TRUE;
				opted = FALSE;
				break;
			}
			case '?':
			case 'h':
			{
				xkeylog_usage();
				break;
			}
		}
	}

	if (opted)
		exit(EXIT_SUCCESS);
}

static void xkeylog_reklama(void)
{
	printf("\n");
	printf(LIGHT_PURPLE_COLOR "====================================================" NORMAL_COLOR "\n");
	printf(LIGHT_PURPLE_COLOR ">>> " LIGHT_PURPLE_COLOR "Please visit " RED_COLOR "http://www.xkeylog.ru" LIGHT_BLUE_COLOR " for support" LIGHT_PURPLE_COLOR " <<<" NORMAL_COLOR "\n");
	printf(LIGHT_PURPLE_COLOR "====================================================" NORMAL_COLOR "\n");
	printf("\n");
}

int main(int argc, char *argv[])
{
#ifdef ENABLE_NLS
	setlocale(LC_ALL, "");
	bindtextdomain(PACKAGE, LOCALEDIR);
	bind_textdomain_codeset(PACKAGE, "UTF-8");
	textdomain(PACKAGE);
#endif

	
	xkeylog_reklama();

	xkeylog_get_options(argc, argv);

	xconfig = xkeylog_config_init();
	if (xconfig == NULL)
	{
		log_message(ERROR, _("Can't init libxnconfig"));
		exit(EXIT_FAILURE);
	}

	xkeylog_set_lock();
	xkeylog_load_config(FALSE);

	xprogram = xprogram_init();
	if (xprogram == NULL)
	{
		log_message(ERROR, _("Failed to init program structure"));
		xconfig->set_pid(xconfig, 0);
		xconfig->uninit(xconfig);
		exit(EXIT_FAILURE);
	}

	xkeylog_init();

	log_message(DEBUG, _("Init program structure complete"));
	
	xntrap(SIGTERM, xkeylog_terminate);
	xntrap(SIGINT, xkeylog_terminate);
	xntrap(SIGHUP, xkeylog_reload);

	xprogram->process_input(xprogram);

	xkeylog_cleanup();

	exit(EXIT_SUCCESS);
}
